package com.example.demo.controller;

import com.example.demo.config.PlcDataWebSocketHandler;
import com.example.demo.pojo.PlcData;
import com.example.demo.service.PlcService;
import com.example.demo.utils.PlcRead;
import com.example.demo.utils.TimeUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.PostConstruct;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("plc-data")
public class PlcUseController {
    private static final Logger logger= LogManager.getLogger(PlcUseController.class);
    @Autowired
    PlcService plcService;
    // 添加静态ObjectMapper实例
    private static final ObjectMapper objectMapper = new ObjectMapper();
    private ExecutorService dbExecutor;
    private ExecutorService plcExecutor;
    private final AtomicBoolean running = new AtomicBoolean(true);
    private volatile PlcData latestPlcData;
    private static final long MAX_ALLOWED_TIME = 500;
    private static final long PLC_READ_TIMEOUT = 300;
    private static final long DB_WRITE_TIMEOUT = 200;

    @Autowired
    private PlcDataWebSocketHandler webSocketHandler;//注入websocket处理器
    /**
     * 初始化线程
     */
    @PostConstruct
    public void init() {
        dbExecutor = new ThreadPoolExecutor(
                5,  // 提高核心线程数
                10, // 提高最大线程数
                60L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100),  // 使用有界队列
                new ThreadFactory() {
                    private int count = 0;
                    @Override
                    public Thread newThread(Runnable r) {
                        Thread thread = new Thread(r);
                        thread.setName("db-executor-" + (count++));
                        thread.setDaemon(true);
                        return thread;
                    }
                }
        );

        plcExecutor = Executors.newSingleThreadExecutor(runnable -> {
            Thread thread = new Thread(runnable);
            thread.setName("plc-reader");
            thread.setDaemon(true);
            return thread;
        });
    }

    /**
     * 监听读取数据和插入数据库的方法
     */
    @EventListener(ApplicationReadyEvent.class)
    public void onApplicationReady() {
        plcExecutor.submit(this::plcReadWithStrictTimeLimit);
    }
    /**
     * 读取数据和插入数据库的方法
     */
    public void plcReadWithStrictTimeLimit() {
        PlcRead plcRead = new PlcRead();

        while (running.get()) {
            //总体开始时间
            long totalStartTime = System.currentTimeMillis();
            //plc读取时间
            long plcReadTime = 0;
            //Mysql写入时间
            long dbWriteTime = 0;

            try {
                // 读取PLC数据（带超时控制）
                long plcStart = System.currentTimeMillis();
                Future<PlcData> plcFuture = CompletableFuture.supplyAsync(plcRead::PlcOpc_read);
                // 关键修复：在此处声明为final并赋值，确保只赋值一次
                final PlcData plcData;
                try {
                    plcData = plcFuture.get(PLC_READ_TIMEOUT, TimeUnit.MILLISECONDS);
                    this.latestPlcData = plcData;
                } catch (TimeoutException e) {
                    logger.error("PLC读取超时！已超过" + PLC_READ_TIMEOUT + "ms");
//                    System.err.println("PLC读取超时！已超过" + PLC_READ_TIMEOUT + "ms");
                    plcFuture.cancel(true);
                    continue;
                }
                plcReadTime = System.currentTimeMillis() - plcStart;

                if (plcData == null) {
                    logger.info("PLC数据为空，跳过本次操作");
//                    System.out.println("PLC数据为空，跳过本次操作");
                    continue;
                }

                // 设置时间戳
                plcData.setEmergency_stop_status_value_time(TimeUtils.getCurrentTimestamp());

                // 读取状态
                int readStatus = plcData.getReadStatus();
                if (readStatus == 1) {
                    // 并行执行：数据库写入 + 前端推送
                    long dbStart = System.currentTimeMillis();
                    try {
                        CompletableFuture<Void> dbFuture = CompletableFuture.runAsync(() -> plcService.insert(plcData), dbExecutor);
                        dbFuture.get(DB_WRITE_TIMEOUT, TimeUnit.MILLISECONDS);
                        // 实时推送WebSocket数据
                        webSocketHandler.sendPlcDataToAll(plcData);
                    } finally {
                        dbWriteTime = System.currentTimeMillis() - dbStart;
                    }
                } else {
                    logger.info("读取出错，数据库不插入数据 ");
//                    System.out.println("读取出错，数据库不插入数据 ");
                        }

            } catch (TimeoutException e) {
                logger.error("数据库操作或前端推送超时！已超过" + DB_WRITE_TIMEOUT + "ms");
//                System.err.println("数据库操作或前端推送超时！已超过" + DB_WRITE_TIMEOUT + "ms");
            } catch (Exception e) {
                logger.error("操作异常: " + e.getMessage());
//                System.err.println("操作异常: " + e.getMessage());
            }

            // 计算总耗时
            long totalTime = System.currentTimeMillis() - totalStartTime;

            // 严格监控耗时
            if (totalTime > MAX_ALLOWED_TIME) {
                logger.error("警告：总耗时超标！总耗时=" + totalTime + "ms " +
                        "(读PLC=" + plcReadTime + "ms, 写数据库=" + dbWriteTime + "ms)");
//                System.err.println("警告：总耗时超标！总耗时=" + totalTime + "ms " +
//                        "(读PLC=" + plcReadTime + "ms, 写数据库=" + dbWriteTime + "ms)");
            } else {
                logger.info("操作正常：总耗时=" + totalTime + "ms " +
                        "(读PLC=" + plcReadTime + "ms, 写数据库=" + dbWriteTime + "ms)");
//                System.out.println("操作正常：总耗时=" + totalTime + "ms " +
//                        "(读PLC=" + plcReadTime + "ms, 写数据库=" + dbWriteTime + "ms)");
            }
            // 控制循环频率
            //如果时间还长则进行休眠
            long sleepTime = Math.max(0, MAX_ALLOWED_TIME - totalTime);
            if (sleepTime > 0) {
                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }
    }

    /**
     * 关闭资源
     * @throws Throwable
     */
    @Override
    protected void finalize() throws Throwable {
        running.set(false);
        dbExecutor.shutdown();
        plcExecutor.shutdown();
        super.finalize();
    }

    /**
     * 提供给前端的接口，用于获取最新的PLC数据
     */
//    @GetMapping("/latestData")
//    public PlcData getLatestPlcData() {
//        return latestPlcData;
//    }


    //main函数测试
//    public static void main(String[] args) {
//        PlcRead plcRead = new PlcRead();
//        PlcData plcData = plcRead.PlcOpc_read();
//        int readStatus = plcData.getReadStatus();
//        if (readStatus == 1) {
//            //执行插入
//            logger.info("插入成功");
////            System.out.println("插入成功");
//        }else{
//            logger.error("plc读取出现问题,sql插入未执行");
////            System.err.println("plc读取出现问题,sql插入未执行");
//        }
//        System.out.println("plcData.toString() = " + plcData.toString());
//    }
}